package preprocessing;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermFreqVector;

public class NGD_Combinations {

	public HashMap<String, Float> termMap;
	
	private ByteBuffer auxBuf;

	public NGD_Combinations() {
		super();
		this.auxBuf=ByteBuffer.allocateDirect(500*1024*1024);
	}


	private int factorial(int num){
		int MAX=num+1;
		BigInteger e[][] = new BigInteger[MAX][MAX];
		for (int i = 0; i < MAX; i++)
        {
            for (int j = 0; j <= 3; j++)
            {
                int n = i;
                int k = j;
                if( k == 0 || n == k )
                {
                    e[n][k] = BigInteger.ONE;
                }
                else if( k == 1 )
                {
                    e[n][k] = new BigInteger(n+"");
                }
                else if( k > n )
                {
                    e[n][k] = BigInteger.ZERO;
                }
                else
                {
                    e[n][k] = e[n-1][k-1].add( e[n-1][k] );
                }
            }
        }
        //System.out.println(e[MAX-1][2]);
		return e[MAX-1][2].intValue();
	}
	
	private HashMap<String, Float> comMap;
	/**
	 * Prepare the HashMap with all possible terms combinations. this method initialize the comMap with all pairs with value equals to 0
	 * @throws IOException 
	 */
	public void init(String folderPath) throws IOException{
		this.comMap=new HashMap<String, Float>(factorial(this.termMap.size()));
		Object[] auxArr=this.termMap.keySet().toArray();
		String [] termsArr=new String[auxArr.length];
		float [] termsVal=new float[auxArr.length];
		for (int i=0;i<auxArr.length;i++)
		{
			termsArr[i]=auxArr[i].toString();
			termsVal[i]=this.termMap.get(termsArr[i]);
		}
		//Define all the possible combination of (numC2)
		String root="";
		for (int i=0;i<termsArr.length;i++)
		{
			root=termsArr[i];
			if (i+1<termsArr.length)
			{
				for (int j=i+1;j<termsArr.length;j++)
				{
					if(root.compareTo(termsArr[j])<0){
						//Root comes alphabetically before than..
						this.comMap.put(root+","+termsArr[j], new Float(0));
					}else
					{
						this.comMap.put(termsArr[j]+","+root, new Float(0));
					}
				}
			}
		}
		storeNGDterms(folderPath, this.termMap);
	}
	
	
	/**
	 * This method receives and index reader an per documents get the terms and recalculate que pairs terms values
	 * @throws IOException 
	 */
	public void calculateTermsPairsValues(IndexReader reader, int numDocs,String folderPath) throws IOException{
		TermFreqVector tfv;
		String[] termStringArr;
		for(int i=0;i<numDocs;i++){
			tfv=reader.getTermFreqVector(i, "body");
			/*
			 * Due a strange error where a sentence in the file LA011090-0118 sentences number:58,
			 * where the sentence body is "but", a new rule was created. the process only finish if
			 * tfv!=null 
			 */
			if(tfv!=null&&tfv.size()>0){
				/*Old line where it is intended to store the complete id. Now only the sentence number (Document Number) in the index is stored
				 * termStringArr=new String[tfv.size()];
				 */
				termStringArr=tfv.getTerms();
				
				//Tasks for NGD
				calculateCombinations(termStringArr);
			}
		}
		refineCombinations();
		storeNGDFilePairs(folderPath);
	}
	
	
	/**
	 * Modify the HasmMap deleting the term with value equals to 0
	 */
	private void refineCombinations(){
	String[] usr_keys=new String [this.comMap.size()];
	this.comMap.keySet().toArray(usr_keys);
	Float[] usr_values=new Float[this.comMap.size()];
	this.comMap.values().toArray(usr_values);
	for(int w=0;w<usr_values.length;w++){
		if(!(usr_values[w]>0)){
			this.comMap.remove(usr_keys[w]);
		}
	}
	}
	private float freqValue=0;
	/**
	 * This method accepts a terms of sentence an recalculate the values in the comMap. 
	 */
	private void calculateCombinations(String[] sentTermArr){
		for(int i=0;i<sentTermArr.length;i++){
			if(i+1<sentTermArr.length)
			{
				for(int j=i+1;j<sentTermArr.length;j++)
				{
					if(sentTermArr[i].compareTo(sentTermArr[j])<0)
					{
						//sentTermArr[i] comes alphabetically before than..
						this.freqValue=this.comMap.get(sentTermArr[i]+","+sentTermArr[j]);
						this.freqValue=this.freqValue+1;
						this.comMap.put(sentTermArr[i]+","+sentTermArr[j], this.freqValue);
					}else
					{
						this.freqValue=this.comMap.get(sentTermArr[j]+","+sentTermArr[i]);
						this.freqValue=this.freqValue+1;
						this.comMap.put(sentTermArr[j]+","+sentTermArr[i], this.freqValue);
					}
				}
			}
			
		}
	}
	/**
	 * This method receives a String returns the ByteBuffer that represents that String.
	 * @param String String to be transformed.
	 * @return ByteByffer ByteBuffer representation of the String. 
	 */
	private static Charset charset = Charset.forName("ISO8859_1");
	private static CharsetEncoder encoder = charset.newEncoder();
	public ByteBuffer stringToBBuffer(String msg){
		try{
		    return encoder.encode(CharBuffer.wrap(msg));
		  }catch(Exception e){e.printStackTrace();}
		  return null;
	}
	/**
	 * 
	 */
	private void storeNGDFilePairs(String folderPath) throws IOException
	{
		System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!GUARDANDO PAREJAS");
		FileOutputStream fos3 = new FileOutputStream(folderPath+"\\gnd_pairs.txt",true);
		FileChannel f = fos3.getChannel();
		
		//
		Object[] keys = this.comMap.keySet().toArray();
        Object[] values = this.comMap.values().toArray();
		//
		int numElemMap=keys.length;
		System.out.println("tam "+numElemMap+"   Len"+values.length);
		int w=0;
		String termString="";
		for (w=0;w<numElemMap-1;w++){
			termString=keys[w].toString()+"\t"+values[w].toString()+"\n";
			this.auxBuf.put(stringToBBuffer(termString));
			if(this.auxBuf.position()>950*1024*1024){
				//Writes only the valid information in the buffer
				this.auxBuf.flip();
				f.write(this.auxBuf);
				this.auxBuf.clear();
				break;
			}
		}
		termString=(String)keys[w]+"\t"+values[w].toString();
		this.auxBuf.put(stringToBBuffer(termString));
		this.auxBuf.flip();
		f.write(this.auxBuf);
		this.auxBuf.clear();
		f.close();
		fos3.close();
		termString="";
	}
	/**
	 * @throws FileNotFoundException 
	 * 
	 */
	private FileChannel f;
	public void openSentencesTermFile(String folderPath) throws FileNotFoundException{
		FileOutputStream fos3 = new FileOutputStream(folderPath+"\\gnd_sent-term.txt",true);
		this.f = fos3.getChannel();
	}
	/**
	 *  
	 * 
	 */
	private ByteBuffer sentTermBuff=ByteBuffer.allocateDirect(200*1024*1024);
	private String sentTermsChain="";
	public void saveSentencesTerm(String[] sentTerm,int lastBool) throws IOException{
		this.sentTermsChain="";
		for(int w=0;w<sentTerm.length;w++){
			if(w==sentTerm.length-1){
				if(lastBool==1){
					this.sentTermsChain+=sentTerm[w];
				}else{
					this.sentTermsChain+=sentTerm[w]+"\n";
				}
				
			}else{
				this.sentTermsChain+=sentTerm[w]+"/";
			}
			
		}
		this.sentTermBuff.put(stringToBBuffer(this.sentTermsChain));
		if(this.sentTermBuff.position()>180*1024*1024||lastBool==1){
			this.sentTermBuff.flip();
			this.f.write(this.sentTermBuff);
			this.sentTermBuff.clear();
		}
	}
	/**
	 * 
	 */
	private void storeNGDterms(String folderPath,Map termsMap) throws IOException{
		FileWriter fstream=new FileWriter(folderPath+"\\gnd_terms.txt",true);
		BufferedWriter out= new BufferedWriter(fstream);
		String termString="";
		int linecounter=0;
		for(Object usr_key:termsMap.keySet()){
			if(linecounter==termsMap.size()-1)
			{
				termString+=usr_key.toString()+"\t"+termsMap.get(usr_key.toString());
			}else
			{
				termString+=usr_key.toString()+"\t"+termsMap.get(usr_key.toString())+"\n";
			}
			linecounter++;
			if(linecounter%100000==0)
			{
				out.write(termString);
				termString="";
			}
		}
		out.write(termString);
		out.close();
		termString="";
	}
	public HashMap<String,Float> getComMap() {
		return comMap;
	}
	public void setComMap(HashMap<String, Float> comMap) {
		this.comMap = comMap;
	}

}
